package framework.proxy;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import javax.persistence.Column;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 基于动态代理实现的数据库动态实体，用于将对象属性映射到字典
 * 通过拦截get/set方法调用，将属性存取操作转发到内部Map存储
 * 注意：不处理类中 boolean 类型, 而应使用包装类型 Boolean
 */
public class DynamicEntityProxy implements MethodInterceptor {
    private static final ConcurrentHashMap<String, String> COLUMN_CACHE = new ConcurrentHashMap<>();
    private final Map<String, Object> map = new HashMap<>();

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        String name = method.getName();
        if (name.startsWith("get") && args.length == 0) {
            String columnName = resolveColumnName(method);
            return map.get(columnName);
        } else if (name.startsWith("set") && args.length == 1) {
            String columnName = resolveColumnName(method);
            map.put(columnName, args[0]);
            return null;
        } else if (name.equals("properties")) {
            return map;
        } else if (name.startsWith("is") && method.getReturnType() == boolean.class) {
            throw new RuntimeException(name + " not supported, not allow use boolean type or modify it to Boolean type");
        }
        // other
        return proxy.invokeSuper(obj, args);
    }

    private String resolveColumnName(Method method) {
        Class<?> clazz = method.getDeclaringClass();
        String fn = method.getName().substring(3);
        final String fieldName = Character.toLowerCase(fn.charAt(0)) + fn.substring(1);
        return COLUMN_CACHE.computeIfAbsent(clazz.getName() + "#" + fieldName,
                k -> resolveColumnName(clazz, method, fieldName)
        );
    }

    private String resolveColumnName(Class<?> clazz, Method method, String fieldName) {
        // 1. 尝试从字段获取
        try {
            Field field = clazz.getDeclaredField(fieldName);
            Column fieldColumn = field.getAnnotation(Column.class);
            if (fieldColumn != null) {
                return fieldColumn.name();
            }
        } catch (NoSuchFieldException ignored) {
        }

        // 2. 尝试从方法获取
        Column methodColumn = method.getAnnotation(Column.class);
        if (methodColumn != null) {
            return methodColumn.name();
        }

        // 3. 默认返回字段名（无注解时）
        return fieldName;
    }
}
